﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Simple.Framework.Configuration;
using Simple.Framework.Logger;
using Simple.Tools;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

/*********************************************************
 * 命名空间 Simple.Framework.Ioc
 * 接口名称 ObjectBuilder
 * 开发人员：zdm
 * 创建时间：2023/11/24 10:08:28
 * 描述说明：
 * 更改历史：
 * 
 * *******************************************************/
namespace Simple.Framework.Ioc
{
	internal class ObjectBuilder:IObjectBuilder
	{
		public ConcurrentDictionary<string, TypeMapper> typeMapperDic { get; set; }

		private IContainer container;
		public ObjectBuilder(IContainer container)
		{
			typeMapperDic = new ConcurrentDictionary<string, TypeMapper>();
			this.container=container;
			
		}	
		/// <summary>
		/// 
		/// </summary>
		/// <param name="constructor"></param>
		/// <returns></returns>
		/// <exception cref="Exception"></exception>
		public object[] CreateParameters(ConstructorInfo constructor)
		{
			if (constructor == null)
				throw new Exception("获取构造方法失败,请检查是否将接口注入到容器中,接口不具有构造方式!");
			object[] Params = new object[constructor.GetParameters().Count()];
			int i = 0;
			foreach (var Parameter in constructor.GetParameters())
			{
				string token = string.Empty;
				if (Parameter.IsDefined(typeof(InstanceKeyAttribute)))
				{
					token = Parameter.GetCustomAttribute<InstanceKeyAttribute>().GetKey();
				}
				Params[i] = GetService(Parameter.ParameterType, token);
				i++;
			}
			return Params;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="Type"></param>
		/// <param name="o"></param>
		public void CreateProperties(Type Type, object o, Type AutowiredType)
		{
			foreach (var Property in Type.GetProperties(
				 BindingFlags.NonPublic
				| BindingFlags.Instance
				| BindingFlags.Public).
				Where(p => p.IsDefined(AutowiredType)))
			{
				string token = string.Empty;
				if (Property.IsDefined(typeof(InstanceKeyAttribute)))
				{
					token = Property.GetCustomAttribute<InstanceKeyAttribute>().GetKey();
				}
				var propValue = GetService(Property.PropertyType, token);
				if (propValue==null)
				{
					propValue = CreateConfigurationByAutowired(Property);
				}
				Property.SetValue(o,propValue);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="target"></param>
		/// <returns></returns>
		public object Create(Type target, string token)
		{
			if (target.IsDefined(typeof(ConfigurationAttribute)))
			{
				return CreateConfiguration(target);
			}
			object o = null;
			TypeMapper typeMapper = FindTargetType(target, token);
			if (typeMapper != null)
			{
				//初始化构造方法
				if (token == string.Empty)
				{
					if (target.FullName!= typeMapper.TargetKey && target.FullName!=typeMapper.Original.FullName)
					{
						token = target.FullName;
					}
					else
					{
						token = typeMapper.TargetKey;

					}
				}
				//单例就返回
				o = typeMapper.GetInstance(token);
				if (typeMapper.LifeCycle == eLifeCycle.Single && o != null)
				{
					return o;
				}
				object[] oParams = CreateParameters(typeMapper.GetTargetType(token).GetConstructors().FirstOrDefault());
				o = Activator.CreateInstance(typeMapper.GetTargetType(token), oParams);
				//配置属性
				if (typeMapper.IsAutowired)
				{
					CreateProperties(typeMapper.GetTargetType(token), o, typeMapper.AutowiredType);
				}
				if (typeMapper.LifeCycle == eLifeCycle.Single)
				{
					typeMapper.SetInstance(token, o);
				}
			}
			return o;
		}

		/// <summary>
		/// 构造配置属性
		/// </summary>
		/// <param name="target"></param>
		/// <returns></returns>
		public object CreateConfiguration(Type target)
		{
			var configuration=this.container.GetService<IConfiguration>();
			ConfigurationAttribute attrCfg =target.GetCustomAttribute<ConfigurationAttribute>();
			var keys=attrCfg.GetName().StringToArray('.');
			return configuration.GetValue(keys, target);
		}

		public object CreateConfigurationByAutowired(PropertyInfo target)
		{
			var configuration = this.container.GetService<IConfiguration>();
			AutowiredAttribute attrCfg = target.GetCustomAttribute<AutowiredAttribute>();
			object o = null;
			if (attrCfg!=null)
			{
				var keys = attrCfg.GetToken().StringToArray('.');
				o= configuration.GetValue(keys, target.PropertyType);
			}
			return  o;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="target"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		public object GetService(Type target, string token)
		{
			if (target == typeof(IContainer) || target == typeof(Container))
			{
				return container;
			}

			//ImplementedInterfaces
			if (target.IsAssignableTo(typeof(Microsoft.Extensions.Logging.ILogger)))
			{
				return CreateLogger(target,token);
			}

			return Create(target, token);
		}
		public object CreateLogger(Type target,string token)
		{
			var provider = (ClassNameLoggerProvider)this.container.GetService<ILoggerProvider>();

			if (target.GetGenericArguments().Any())
			{
				token = target.GetGenericArguments()[0].FullName;
			}

			if (target.IsGenericType)
			{
				var GenericTypeDefinition=target.GetGenericTypeDefinition();
				if (GenericTypeDefinition == typeof(Logger<>))
				{
					return provider.CreateLogger(target,token, target.GetGenericArguments()[0]);
				}
				return provider.CreateLogger(token, target.GetGenericArguments()[0]);
			}
			else
			{
				return provider.CreateLogger(token);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="Target"></typeparam>
		/// <returns></returns>
		public IEnumerable<Target> GetServiceAll<Target>()
		{
			Type targetType = typeof(Target);

			var typeMapper = FindTargetType(targetType, string.Empty);
			if (typeMapper != null)
			{
				List<Target> targes = new List<Target>();

				foreach (var item in typeMapper.Target)
				{
					targes.Add((Target)GetService(item.Value.Item1, item.Key));
				}
				return targes;
			}
			return default;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="target"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		private TypeMapper FindTargetType(Type target, string token)
		{
			if (ToolSimpleCheck.IsNullOrEmpty(token))
			{
				token = target.FullName;
			}
			TypeMapper typeMapper = null;
			if (typeMapperDic.ContainsKey(token))
			{
				typeMapper = typeMapperDic[token];
			}
			else if (typeMapperDic.Any(Mapper => Mapper.Value.Target.ContainsKey(token)))
			{
				typeMapper = typeMapperDic.FirstOrDefault(Mapper => Mapper.Value.Target.ContainsKey(token)).Value;
			}
			return typeMapper;
		}
	}
}
